/* * Author: Chris Seguin * * This software has been developed under the copyleft * rules of the GNU General Public License. Please * consult the GNU General Public License for more * details about use and distribution of this software. */ package org.acm.seguin.refactor.field; import java.util.Iterator; import java.util.StringTokenizer; import org.acm.seguin.parser.ChildrenVisitor; import org.acm.seguin.parser.ast.ASTArguments; import org.acm.seguin.parser.ast.ASTConstructorDeclaration; import org.acm.seguin.parser.ast.ASTFieldDeclaration; import org.acm.seguin.parser.ast.ASTMethodDeclaration; import org.acm.seguin.parser.ast.ASTName; import org.acm.seguin.parser.ast.ASTPackageDeclaration; import org.acm.seguin.parser.ast.ASTPrimaryExpression; import org.acm.seguin.parser.ast.ASTPrimaryPrefix; import org.acm.seguin.parser.ast.ASTPrimarySuffix; import org.acm.seguin.parser.ast.ASTUnmodifiedClassDeclaration; import org.acm.seguin.parser.ast.ASTUnmodifiedInterfaceDeclaration; import org.acm.seguin.parser.ast.ASTVariableDeclarator; import org.acm.seguin.parser.ast.ASTVariableDeclaratorId; import org.acm.seguin.summary.LocalVariableSummary; import org.acm.seguin.summary.MethodSummary; import org.acm.seguin.summary.PackageSummary; import org.acm.seguin.summary.ParameterSummary; import org.acm.seguin.summary.Summary; import org.acm.seguin.summary.TypeSummary; import org.acm.seguin.summary.VariableSummary; import org.acm.seguin.summary.query.GetMethodSummary; import org.acm.seguin.summary.query.GetTypeSummary; import org.acm.seguin.summary.query.ImportsType; import org.acm.seguin.summary.query.LookupVariable; /** * Visitor that traverses an AST and removes a specified field * *@author Chris Seguin */ public class RenameFieldVisitor extends ChildrenVisitor { /** * Visit a package declaration * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTPackageDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; ASTName name = (ASTName) node.jjtGetChild(0); PackageSummary packageSummary = PackageSummary.getPackageSummary(name.getName()); rfd.setCurrentSummary(packageSummary); return super.visit(node, data); } /** * Visit a class declaration * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTUnmodifiedClassDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; Summary current = rfd.getCurrentSummary(); if (current == null) { rfd.setCurrentSummary(GetTypeSummary.query("", node.getName())); } else if (current instanceof PackageSummary) { rfd.setCurrentSummary(GetTypeSummary.query((PackageSummary) current, node.getName())); } else if (current instanceof TypeSummary) { rfd.setCurrentSummary(GetTypeSummary.query((TypeSummary) current, node.getName())); } else if (current instanceof MethodSummary) { rfd.setCurrentSummary(GetTypeSummary.query((MethodSummary) current, node.getName())); } Object result = super.visit(node, data); rfd.setCurrentSummary(current); return result; } /** * Visit a class declaration * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTUnmodifiedInterfaceDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; Summary current = rfd.getCurrentSummary(); if (current == null) { rfd.setCurrentSummary(GetTypeSummary.query("", node.getName())); } else if (current instanceof PackageSummary) { rfd.setCurrentSummary(GetTypeSummary.query((PackageSummary) current, node.getName())); } else if (current instanceof TypeSummary) { rfd.setCurrentSummary(GetTypeSummary.query((TypeSummary) current, node.getName())); } else if (current instanceof MethodSummary) { rfd.setCurrentSummary(GetTypeSummary.query((MethodSummary) current, node.getName())); } Object result = super.visit(node, data); rfd.setCurrentSummary(current); return result; } /** * Visit a field declaration * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTFieldDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; if (rfd.getCurrentSummary() == rfd.getTypeSummary()) { for (int ndx = 1; ndx < node.jjtGetNumChildren(); ndx++) { ASTVariableDeclarator next = (ASTVariableDeclarator) node.jjtGetChild(ndx); ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) next.jjtGetChild(0); if (id.getName().equals(rfd.getOldName())) { id.setName(rfd.getNewName()); } } } return super.visit(node, data); } /** * Visit a primary expression * *@param node the class body node *@param data the data for the visitor *@return the field if it is found */ public Object visit(ASTPrimaryExpression node, Object data) { RenameFieldData rfd = (RenameFieldData) data; ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) node.jjtGetChild(0); if ("this".equals(prefix.getName())) { processThisExpression(rfd, node, prefix); } else if ((prefix.jjtGetNumChildren() >= 1) && (prefix.jjtGetChild(0) instanceof ASTName)) { processNameExpression(rfd, node, prefix); } return super.visit(node, data); } /** * Visit a method declaration * *@param node Description of Parameter *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTMethodDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; Summary current = rfd.getCurrentSummary(); MethodSummary found = GetMethodSummary.query((TypeSummary) current, node); rfd.setCurrentSummary(found); rfd.setMustInsertThis(isAlreadyPresent(found, rfd.getNewName())); boolean thisRequired = LookupVariable.getLocal(found, rfd.getOldName()) != null; rfd.setThisRequired(thisRequired); Object result = super.visit(node, data); rfd.setThisRequired(false); rfd.setCurrentSummary(current); return result; } /** * Visit a constructor declaration * *@param node Description of Parameter *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTConstructorDeclaration node, Object data) { RenameFieldData rfd = (RenameFieldData) data; Summary current = rfd.getCurrentSummary(); MethodSummary found = GetMethodSummary.query((TypeSummary) current, node); rfd.setCurrentSummary(found); rfd.setMustInsertThis(isAlreadyPresent(found, rfd.getNewName())); boolean thisRequired = LookupVariable.getLocal(found, rfd.getOldName()) != null; rfd.setThisRequired(thisRequired); Object result = super.visit(node, data); rfd.setThisRequired(false); rfd.setCurrentSummary(current); return result; } /** * Determine if the new name is already present in the method * *@param method Description of Parameter *@param newName Description of Parameter *@return The AlreadyPresent value */ private boolean isAlreadyPresent(MethodSummary method, String newName) { Iterator iter = method.getParameters(); if (iter != null) { while (iter.hasNext()) { ParameterSummary next = (ParameterSummary) iter.next(); if (next.getName().equals(newName)) { return true; } } } iter = method.getDependencies(); if (iter != null) { while (iter.hasNext()) { Summary next = (Summary) iter.next(); if ((next instanceof LocalVariableSummary) && (next.getName().equals(newName))) { return true; } } } return false; } /** * Description of the Method * *@param name Description of Parameter *@param oldName Description of Parameter *@param current Description of Parameter *@param hasSuffixArguments Description of Parameter *@param changingType Description of Parameter *@return Description of the Returned Value */ private int shouldChangePart(ASTName name, String oldName, Summary current, boolean hasSuffixArguments, TypeSummary changingType) { int last = name.getNameSize() - 1; if (hasSuffixArguments) { last--; } int forwardTo = -1; for (int ndx = last; ndx >= 0; ndx--) { if (name.getNamePart(ndx).equals(oldName)) { forwardTo = ndx; } } if (forwardTo == -1) { return -1; } VariableSummary varSummary = LookupVariable.query((MethodSummary) current, name.getNamePart(0)); if (varSummary == null) { return -1; } TypeSummary currentType = GetTypeSummary.query(varSummary.getTypeDecl()); for (int ndx = 1; ndx < forwardTo; ndx++) { varSummary = LookupVariable.queryFieldSummary(currentType, name.getNamePart(ndx)); if (varSummary == null) { return -1; } currentType = GetTypeSummary.query(varSummary.getTypeDecl()); } if (currentType == changingType) { return forwardTo; } return -1; } /** * Description of the Method * *@param rfd Description of Parameter *@param node Description of Parameter *@param prefix Description of Parameter */ private void processThisExpression(RenameFieldData rfd, ASTPrimaryExpression node, ASTPrimaryPrefix prefix) { if (rfd.isAllowedToChangeThis() && (node.jjtGetNumChildren() >= 2)) { ASTPrimarySuffix suffix = (ASTPrimarySuffix) node.jjtGetChild(1); if (rfd.getOldName().equals(suffix.getName())) { boolean change = true; if (node.jjtGetNumChildren() >= 3) { ASTPrimarySuffix next = (ASTPrimarySuffix) node.jjtGetChild(2); if ((next.jjtGetChild(0) != null) && (next.jjtGetChild(0) instanceof ASTArguments)) { change = false; } } if (change) { suffix.setName(rfd.getNewName()); } } } } /** * Description of the Method * *@param rfd Description of Parameter *@param node Description of Parameter *@param prefix Description of Parameter */ private void processNameExpression(RenameFieldData rfd, ASTPrimaryExpression node, ASTPrimaryPrefix prefix) { ASTName name = (ASTName) prefix.jjtGetChild(0); if (!rfd.isThisRequired()) { boolean hasSuffixArguments = false; if (node.jjtGetNumChildren() >= 2) { ASTPrimarySuffix next = (ASTPrimarySuffix) node.jjtGetChild(1); if ((next.jjtGetChild(0) != null) && (next.jjtGetChild(0) instanceof ASTArguments)) { hasSuffixArguments = true; } } if ((name.getNameSize() > 1) || !hasSuffixArguments) { if ((rfd.isAllowedToChangeFirst()) && (name.getNamePart(0).equals(rfd.getOldName()))) { name.setNamePart(0, rfd.getNewName()); if (rfd.isMustInsertThis()) { name.insertNamePart(0, "this"); } } else { int index = shouldChangePart(name, rfd.getOldName(), rfd.getCurrentSummary(), hasSuffixArguments, rfd.getTypeSummary()); if (index > -1) { name.setNamePart(index, rfd.getNewName()); } } } } if (rfd.getOldField().getModifiers().isStatic()) { String nameString = name.getName(); if (nameString.startsWith(rfd.getFullName())) { replaceNamePart(name, rfd.getFullName(), rfd.getNewName()); } else if (nameString.startsWith(rfd.getImportedName()) && ImportsType.query(rfd.getCurrentSummary(), rfd.getTypeSummary())) { replaceNamePart(name, rfd.getImportedName(), rfd.getNewName()); } } } /** * Description of the Method * *@param name Description of Parameter *@param form Description of Parameter *@param newName Description of Parameter */ private void replaceNamePart(ASTName name, String form, String newName) { StringTokenizer tok = new StringTokenizer(form, "."); int count = -1; String finalPart = null; while (tok.hasMoreTokens()) { finalPart = tok.nextToken(); count++; } if (name.getNamePart(count).equals(finalPart)) { name.setNamePart(count, newName); } } }